/*	$Id: buf.c 6 2008-07-29 02:30:10Z phrakt $	*/
/*
 * Copyright (c) 2003 Jean-Francois Brousseau <jfb@fugusec.net>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/param.h>

#include <err.h>
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

#include "buf.h"
#include "pforge.h"


/* increment size on auto extension */
#define PFBUF_AUTOEXT_INCR    64



struct pfbuf {
	int      flags;
	uint8_t  *start;
	uint8_t  *cur;
	size_t   len;
};



#define SIZE_LEFT(b)  ((size_t)(b->start - b->cur) + b->len)
#define SIZE_USED(b)  ((size_t)(b->cur - b->start))


/*
* pfbuf_create()
*
* Create a new buffer structure and return a pointer to it.  This structure
* uses dynamically-allocated memory and must be freed with pf_buf_free(), after
* the buffer has been used.
*/

struct pfbuf*
pfbuf_create(size_t len, int flags)
{
	struct pfbuf *b;

	b = (struct pfbuf *) malloc(sizeof(*b));
	if (b == NULL) {
		return (NULL);
	}

	b->start = malloc(len);
	if (b->start == NULL) {
		free(b);
		return (NULL);
	}
	memset(b->start, 0, len);

	b->len = len;
	b->cur = b->start;

	return (b);
}


/*
* pfbuf_free()
*
* Free the buffer <b> and all associated data.
*/

void
pfbuf_free(struct pfbuf *b)
{
	free(b->start);
	free(b);
}


/*
* pfbuf_append()
*
* Append <len> bytes of data pointed to by <data> to the buffer <b>.  If the
* buffer is too small to accept all data, it will attempt to append as much
* data as possible, or if the PFBUF_FL_AUTOEXT flag is set for the buffer, it
* will get resized to an appropriate size to accept all data.
*
* Returns the number of bytes successfully appended to the buffer, or -1
* on failure.
*/

ssize_t
pfbuf_append(struct pfbuf *b, const void *data, size_t len)
{
	size_t incr, rlen;
	void *tmp;

	if ((SIZE_LEFT(b) < len) && (b->flags & PFBUF_FL_AUTOEXT)) {
			incr = PFBUF_AUTOEXT_INCR;
			while ((SIZE_LEFT(b) + incr) < len)
				incr += PFBUF_AUTOEXT_INCR;

			/* reallocate so it can hold the whole result */
			tmp = realloc(b->start, b->len + incr);
			if (tmp == NULL)
				return (-1);

			b->start = (uint8_t *) tmp;
			rlen = len;
	} else {
		/* copy as much as possible */
		rlen = MIN(len, SIZE_LEFT(b));
	}

	memcpy(b->cur, data, rlen);
	b->cur += rlen;

	return (rlen);
}


/*
* pfbuf_size()
*
* Returns the size of the buffer that is being used.
*/

size_t
pfbuf_size(struct pfbuf *b)
{
	return (size_t) (b->cur - b->start);
}


/*
* pfbuf_peek()
*
* Peek at the contents of the buffer.
*/

const void*
pfbuf_peek(struct pfbuf *b)
{
	return (b->start);
}


/*
* pfbuf_copy()
*
* Copy the first <len> bytes of data in the buffer <b> starting at offset
* offset in the destination buffer <dst>.
* Returns the number of bytes successfully copied, or -1 on failure.
*/

ssize_t
pfbuf_copy(struct pfbuf *b, void *dst, size_t len, size_t offset)
{
	size_t rc;

	if (offset > SIZE_USED(b)) {
		return (-1);
	}

	rc = MIN(len, (SIZE_USED(b) - offset));

	memcpy(dst, b->start, rc);

	return (ssize_t)rc;
}


/*
* pfbuf_stringify()
*
* NUL-terminate the buffer in <b> so it can be used as a C-string.
* Returns a pointer to the stringified version, or NULL on failure.
*/

const char*
pfbuf_stringify(struct pfbuf *b)
{
	*(b->cur) = '\0';

	return (char *) b->start;
}
